# -*- coding: utf-8 -*-
import datetime
import re
from odoo.osv import expression
from odoo import api, fields, models, _
from odoo.exceptions import UserError, ValidationError

Type = [('oem', 'OEM'), ('ems', 'EMS'), ('trade', 'Trade'), ('pcb_maker', 'PCB Maker')]
Gender = [('man', 'Mr'), ('woman', 'Miss')]
SaleTeamLineType = [('sales_man', 'Sales'), ('assistant', 'Assistant')]


class Partner(models.Model):
    _inherit = "res.partner"

    s_special_customer = fields.Boolean(string="Special Customer", default=False,
                                        help="Only special customers can place orders directly")
    code = fields.Char(string="Code")
    code_tail = fields.Char(string="Code tail", compute="_compute_code_tail")
    short_name = fields.Char(string="Short name")
    parent_compant_id = fields.Many2one("res.partner", string="Parent Company", domain="[('is_company', '=', True)]")
    client_type = fields.Selection(Type, string='Client type')
    type = fields.Selection(default=lambda self: self.env.context.get('default_type', 'contact'))
    position = fields.Char(string="Position")
    gender = fields.Selection(Gender, string='Gender')
    landline = fields.Char(string="Landline")
    other_phone = fields.Char(string="Other ways of contact")
    tax_ids = fields.Many2many('account.tax', string="Tax Rate")
    credit_line = fields.Char(string="Credit line")
    financial_street = fields.Char(string="Street")
    financial_street2 = fields.Char(string="Street2")
    financial_zip = fields.Char(string="Zip", change_default=True)
    financial_city = fields.Char(string="City")
    financial_state_id = fields.Many2one("res.country.state", string='State', ondelete='restrict',
                                         domain="[('country_id', '=?', country_id)]")
    financial_country_id = fields.Many2one('res.country', string='Country', ondelete='restrict')

    is_financial_contact = fields.Boolean('Agent Information', default=False, required=True)
    financial_contact_line_ids = fields.One2many("res.partner.financial.contact.line", 'parent_id',
                                                 string="Financial contact line", )
    contact_child_ids = fields.One2many('res.partner', 'parent_id', string='Contact Contact',
                                        domain=[('active', '=', True), ('is_company', '!=', True),
                                                ('type', '=', 'contact')])
    shipping_child_ids = fields.One2many('res.partner', 'parent_id', string='Shipping Contact',
                                         domain=[('active', '=', True), ('type', '=', 'delivery'),
                                                 ('is_company', '!=', True)])

    incoterm = fields.Many2one('account.incoterms', string='Incoterm', store=True)
    s_incoterm_readonly = fields.Boolean(string="Incoterm Readonly", compute='_compute_s_incoterm_readonly')
    sale_team_line_ids = fields.One2many('res.partner.sale.team.line', 'parent_id', string='Shipping Info List')
    team_id = fields.Many2one(
        'crm.team', 'Sales Team', compute='_compute_team_id', store=True,
        help='If set, this Sales Team will be used for sales and assignments related to this partner')
    is_company = fields.Boolean(default=True)

    is_virtual_customer = fields.Boolean(string="Is Potential Customer", default=False, tracking=True)

    currency_id = fields.Many2one(compute='_compute_currency_id', string="Currency", store=True, readonly=True)
    phone = fields.Char(string="Tel")
    contact_name = fields.Char(string="Contact Name")
    short_name_en = fields.Char(string="Short Name EN")
    short_name_en_region = fields.Char(string="Short Name EN Region")
    short_name_region = fields.Char(string="Short Name Region")
    name_en = fields.Char(string="Name EN")
    s_regular_code = fields.Char(string="JOVE Code")
    s_invoice_print_address = fields.Char(string="Full Address", compute="_compute_financial_full_address")
    s_ship_company_name = fields.Char(string="Company Name")
    s_goods_type = fields.Selection([('semi_fg', 'Semi-FG'), ('fg', 'FG')], string='Goods Type', default='fg')
    property_payment_term_id = fields.Many2one('account.payment.term', company_dependent=True,
                                               string='Customer Payment Terms', tracking=True,
                                               help="This payment term will be used instead of the default one for sales orders and customer invoices")
    property_supplier_payment_term_id = fields.Many2one('account.payment.term', company_dependent=True,
                                                        string='Vendor Payment Terms', tracking=True,
                                                        help="This payment term will be used instead of the default one for purchase orders and vendor bills")
    property_purchase_currency_id = fields.Many2one(
        'res.currency', string="Supplier Currency", company_dependent=True, tracking=True,
        help="This currency will be used, instead of the default one, for purchases from the current partner")

    def _name_search(self, name='', args=None, operator='ilike', limit=100, name_get_uid=None, order=None):
        args = args or []
        domain = ['|', ('name', operator, name), ('code', operator, name)]
        return self._search(expression.AND([domain, args]), limit=limit, access_rights_uid=name_get_uid, order=order)

    # @api.model
    # def get_default_next_customer_code(self):
    #     if self.default_get(['is_company'])['is_company'] and self.env.context.get('market', False):
    #         if self.env.context.get('res_partner_search_mode', False) and self.env.context.get('default_supplier_rank', 0) <= 0 \
    #                 and self.env.context.get('default_is_virtual_customer', False) is not True:
    #             return self.get_next_customer_code()
    #         return False

    @api.model
    def get_next_customer_code(self):
        """
        正式客戶建議編號：YY####A
        YY 為當前年分後兩位，#### 為 4 位流水，每年重計。
        """
        year_short = datetime.datetime.now().strftime('%y')  # 25
        pattern = f'^{year_short}[0-9]{{4}}[A-Za-z]$'  # 250001A

        self.env.cr.execute("""
            SELECT MAX(SUBSTRING(code FROM 3 FOR 4)::int)
            FROM res_partner
            WHERE code ~ %s
              AND is_virtual_customer IS NOT TRUE
              AND COALESCE(supplier_rank, 0) <= 0
              AND is_company IS TRUE
        """, (pattern,))
        max_seq = (self.env.cr.fetchone()[0] or 0)

        next_seq = max_seq + 1
        candidate = f'{year_short}{next_seq:04d}A'

        # 避免併發撞號
        while self.search_count([
            ('code', '=', candidate),
            ('is_virtual_customer', '=', False),
            ('supplier_rank', '<=', 0),
            ('is_company', '=', True),
        ], limit=1):
            next_seq += 1
            candidate = f'{year_short}{next_seq:04d}A'

        return candidate

    @api.onchange('code', 'is_virtual_customer')
    def _onchange_check_code(self):
        """只在用户输入时校验格式，不阻止保存"""
        if self.is_company and not self.supplier_rank > 0:
            if self.code:
                if not self.is_virtual_customer:
                    if not re.fullmatch(r'\d{6}[A-Za-z]', self.code):
                        return {
                            'warning': {
                                'title': _("Invalid Code"),
                                'message': _("Code must be 6 digits followed by 1 letter (e.g. 250025A).")
                            }
                        }
                else:
                    if not re.fullmatch(r'\d{4}[A-Za-z]', self.code):
                        return {
                            'warning': {
                                'title': _("Invalid Virtual Code"),
                                'message': _(
                                    "Virtual customer code must start with 'Q' followed by 4 digits and 1 letter (e.g. Q0025A).")
                            }
                        }

    @api.depends('user_id')
    def _compute_team_id(self):
        for record in self:
            if record.user_id and record.user_id.sale_team_id:
                record.team_id = record.user_id.sale_team_id.id
            else:
                record.team_id = False

    @api.depends('financial_street', 'financial_street2', 'financial_zip', 'financial_city', 'financial_state_id',
                 'financial_country_id')
    def _compute_financial_full_address(self):
        for record in self:
            address_parts = [
                record.financial_street or '',
                record.financial_street2 or '',
                record.financial_zip or '',
                record.financial_city or '',
                record.financial_state_id.name if record.financial_state_id else '',
                record.financial_country_id.name if record.financial_country_id else ''
            ]
            # 使用逗号和空格连接各个部分
            record.s_invoice_print_address = ', '.join(filter(None, address_parts))

    @api.depends('incoterm')
    def _compute_s_incoterm_readonly(self):
        for record in self:
            if record.incoterm and record.incoterm.code not in ['EXW', 'FCA', 'FOB']:
                record.s_incoterm_readonly = True
            else:
                record.s_incoterm_readonly = False

    def action_open_customer_form(self):
        """打开虚拟客户表单：
        - 销售管理员直接放行
        - 客户无跟单员 → 公共客户，任何人可查看
        - 客户有跟单员 → 必须跟单员在当前用户团队
        """
        self.ensure_one()
        # 1. 管理员放行
        if self.env.user.has_group('sales_team.group_sale_manager'):
            return self._open_customer_form()
        # 2. 有跟单员才校验团队；无跟单员视为公共客户，直接放行
        if self.user_id:
            current_team = self.env.user.sale_team_id
            if not current_team or self.user_id not in current_team.member_ids:
                raise UserError(
                    _('The sales person (%s) is not in your sales team.') % self.user_id.name
                )
        # 3. 权限通过，统一打开表单
        return self._open_customer_form()

    def _open_customer_form(self):
        """返回统一的客户表单动作，避免重复代码。"""
        return {
            'name': _('Customer Detail'),
            'view_mode': 'form',
            'type': 'ir.actions.act_window',
            'res_model': 'res.partner',
            'views': [(self.env.ref('sb_partner_inherit.view_sb_partner_form').id, 'form')],
            'res_id': self.id,
            'target': 'current',
        }

    def open_create_client_action(self):
        return {
            'name': 'Create Client',
            'type': 'ir.actions.act_window',
            'res_model': 'res.partner',
            'view_mode': 'form',
            'views': [(self.env.ref('sb_partner_inherit.view_sb_partner_form').id, 'form')],
            'context': self.env.context,
        }

    def action_to_partner(self):
        """将虚拟客户转为正式客户，并弹出提醒"""
        self.ensure_one()
        partner = self

        # 1. 子联系人同步转正
        if partner.shipping_child_ids:
            partner.shipping_child_ids.write({'is_virtual_customer': False})

        # 2. 生成新的正式客户建议编码
        new_code = self.get_next_customer_code()
        partner.write({
            'is_virtual_customer': False,
            'code': new_code,
        })

        # 3. 組合動作：先顯示通知，再打開表單
        return {
            'type': 'ir.actions.client',
            'tag': 'display_notification',
            'params': {
                'title': _('Win Win!😎'),
                'type': 'success',
                'message': _(
                    """Customer converted successfully!\n
                    System Suggested code: %s\n
                    ❗Please double-check the customer code."""
                ) % new_code,
                'sticky': False,
                'next': {
                    'type': 'ir.actions.act_window',
                    'res_model': 'res.partner',
                    'view_mode': 'form',
                    'views': [(self.env.ref('sb_partner_inherit.view_sb_partner_form').id, 'form')],
                    'res_id': partner.id,
                },
            },
        }

    def _handle_virtual_code_prefix(self, vals):
        """
        根据 is_virtual_customer 自动补/删 code 的 Q 前缀
        注意：这个方法会直接修改 vals
        """
        is_virtual = vals.get('is_virtual_customer', self.is_virtual_customer)
        code = vals.get('code', self.code or '')
        is_company = vals.get('is_company', self.is_company)
        if is_virtual and is_company and not code.startswith('Q'):
            vals['code'] = 'Q' + code
        elif not is_virtual and is_company and code.startswith('Q'):
            vals['code'] = code[1:]

    @api.model_create_multi
    def create(self, vals_list):
        for vals in vals_list:
            # 1. 供应商编码自动生成
            if (
                    self.env.context.get('default_supplier_rank', 0) > 0
                    and not vals.get('parent_id')
            ):
                vals['code'] = self.env['ir.sequence'].next_by_code('rs.supplier.code') or '/'
            else:
                if self.env.context.get('market'):
                    if vals.get('is_company', False) and vals.get('supplier_rank', 0) <= 0:
                        vals.setdefault('user_id', self.env.user.id)
                        self._handle_virtual_code_prefix(vals)

        return super(Partner, self).create(vals_list)

    def write(self, values):
        # 需要逐條處理的情境
        if 'code' in values or 'is_virtual_customer' in values:
            need_generic_write = True  # 保險旗標
            for rec in self:
                if rec.is_company and rec.supplier_rank <= 0:
                    vals_copy = values.copy()
                    vals_copy.setdefault('code', rec.code)
                    self._handle_virtual_code_prefix(vals_copy)
                    super(Partner, rec).write(vals_copy)
                    need_generic_write = False  # 已經有寫入
            # 若完全沒有滿足條件的記錄，仍要批量寫入一次
            if need_generic_write:
                super(Partner, self).write(values)
        else:
            super(Partner, self).write(values)

        # 級聯更新 user_id
        if 'user_id' in values:
            new_user = values['user_id']
            # 一次搜尋、一次寫入，減少 SQL
            partners = self.ids
            self.env['sale.order'].sudo().search([('partner_id', 'in', partners)]).write({'user_id': new_user})
            self.env['crm.lead'].sudo().search([('partner_id', 'in', partners)]).write({'user_id': new_user})

        return True

    @api.depends('code')
    def _compute_code_tail(self):
        for record in self:
            if record.code:
                code_parts = record.code.split('-')
                record.code_tail = code_parts[-1] if len(code_parts) > 1 else False
            else:
                record.code_tail = False

    @api.depends('property_product_pricelist')
    def _compute_currency_id(self):
        for record in self:
            record.currency_id = record.property_product_pricelist.currency_id.id if record.property_product_pricelist else self.company_id.currency_id.id

    @api.onchange('country_id', 'state_id', 'city', 'zip', 'street', 'street2')
    def onchange_address(self):
        if self.shipping_child_ids:
            first_child = self.shipping_child_ids[0]
            first_child.write({
                'country_id': self.country_id.id,
                'state_id': self.state_id.id,
                'city': self.city,
                'zip': self.zip,
                'street': self.street,
            })

    @api.onchange('country_id')
    def get_financial_country_id(self):
        for item in self:
            if item.country_id and not item.financial_country_id:
                item.financial_country_id = item.country_id
            if not item.country_id:
                item.financial_country_id = False

    @api.onchange('state_id')
    def get_financial_state_id(self):
        for item in self:
            if item.state_id and not item.financial_state_id:
                item.financial_state_id = item.state_id
                if item.state_id.postal_code:
                    item.zip = item.state_id.postal_code
            if not item.state_id:
                item.financial_state_id = False

    @api.onchange('city')
    def get_financial_city(self):
        for item in self:
            if item.city and not item.financial_city:
                item.financial_city = item.city
            if not item.city:
                item.financial_city = False

    @api.onchange('zip')
    def get_financial_zip(self):
        for item in self:
            if item.zip and not item.financial_zip:
                item.financial_zip = item.zip
                if item.state_id and not item.state_id.postal_code:
                    item.state_id.postal_code = item.zip
            if not item.zip:
                item.financial_zip = False

    @api.onchange('street')
    def get_financial_street(self):
        for item in self:
            if item.street and not item.financial_street:
                item.financial_street = item.street
            if not item.street:
                item.financial_street = False

    @api.onchange('street2')
    def get_financial_street2(self):
        for item in self:
            if item.street2 and not item.financial_street2:
                item.financial_street2 = item.street2
            if not item.street2:
                item.financial_street2 = False

    @api.model
    def default_get(self, fields):
        rec = super(Partner, self).default_get(fields)
        active_model = self.env.context.get('active_model')
        if active_model == 'crm.lead' and len(self.env.context.get('active_ids', [])) <= 1:
            lead = self.env[active_model].browse(self.env.context.get('active_id')).exists()
            if lead:
                rec.update(
                    phone=lead.phone,
                    mobile=lead.mobile,
                    function=lead.function,
                    title=lead.title.id,
                    website=lead.website,
                    street=lead.street,
                    street2=lead.street2,
                    city=lead.city,
                    state_id=lead.state_id.id,
                    country_id=lead.country_id.id,
                    zip=lead.zip,
                )
        return rec


class PartnerFinancialFontactLine(models.Model):
    _name = "res.partner.financial.contact.line"
    _description = "res.partner.financial.contact.line"

    parent_id = fields.Many2one("res.partner", string='partner id', ondelete='cascade')
    name = fields.Char(string="Name", required=True)
    phone = fields.Char(string="Phone Number")
    email = fields.Char(string="Email Address")
    passport = fields.Char(string="Passport Number")
    commission = fields.Float(string="Rabate(%)")
    address = fields.Char(string="Residential Address")

    @api.constrains("commission")
    def _check_commission(self):
        for res in self:
            if res.commission > 1 or res.commission < 0:
                raise ValidationError(_("Commission must be greater than 0 and less than 1"))


class PartnerIncoterm(models.Model):  # not use!!!!!!!!!!!!!!!!!!!!
    _name = "res.partner.incoterm"
    _description = "res.partner.incoterm"

    number = fields.Char(string="Number", required=True)
    name = fields.Char(string="Name", required=True)


class PartnerSalesTeamLine(models.Model):  # not use begen 0828!!!!!!!!!!!!!!!!!!!!
    _name = "res.partner.sale.team.line"
    _description = "res.partner.sale.team.line"

    parent_id = fields.Many2one("res.partner", string='res.partner', ondelete='cascade')
    employee_id = fields.Many2one('hr.employee', string='Name', ondelete='cascade')
    commission = fields.Float(string="Commission", default=0.00)
    type = fields.Selection(SaleTeamLineType, string='Type')


class ResConuntryState(models.Model):
    _inherit = 'res.country.state'

    postal_code = fields.Char(string="Postal Code")
